/**
 * IK 中文分词  版本 5.0
 * IK Analyzer release 5.0
 * <p>
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * <p>
 * 源代码由林良益(linliangyi2005@gmail.com)提供
 * 版权声明 2012，乌龙茶工作室
 * provided by Linliangyi and copyright 2012 by Oolong studio
 */
package org.wltea.analyzer.dic;

import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.wltea.analyzer.cfg.Configuration;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 词典管理类,单子模式
 */
public class Dictionary {


	/*
	 * 词典单子实例
	 */
	private static Dictionary singleton;

	/*
	 * 主词典对象
	 */
	private DictSegment _MainDict;

	/*
	 * 停止词词典
	 */
	private DictSegment _StopWordDict;
	/*
	 * 量词词典
	 */
	private DictSegment _QuantifierDict;

	private static ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
	/**
	 * 配置对象
	 */
	private Configuration cfg;

	private Dictionary(Configuration cfg) {
		this.cfg = cfg;
		//加载主词典
		this.loadMainDict();
		//加载停用词
		this.loadStopWordDict();
		//加载量词
		this.loadQuantifierDict();
	}

	/**
	 * 词典初始化
	 * 由于IK Analyzer的词典采用Dictionary类的静态方法进行词典初始化
	 * 只有当Dictionary类被实际调用时，才会开始载入词典，
	 * 这将延长首次分词操作的时间
	 * 该方法提供了一个在应用加载阶段就初始化字典的手段
	 *
	 * @param cfg a {@link org.wltea.analyzer.cfg.Configuration} object.
	 * @return Dictionary
	 */
	public static Dictionary initial(Configuration cfg) {
		if (singleton == null) {
			synchronized (Dictionary.class) {
				if (singleton == null) {
					singleton = new Dictionary(cfg);

					if (cfg.isEnableRemoteDict()) {
						// 建立监控线程
						for (String location : cfg.getRemoteExtDictionarys()) {
							// 10 秒是初始延迟可以修改的 60是间隔时间 单位秒
							pool.scheduleAtFixedRate(new Monitor(DictType.MainDict, location), 1, 60, TimeUnit.SECONDS);
						}
						for (String location : cfg.getRemoteExtStopWordDictionarys()) {
							pool.scheduleAtFixedRate(new Monitor(DictType.StopDist, location), 1, 60, TimeUnit.SECONDS);
						}
					}

					return singleton;
				}
			}
		}
		return singleton;
	}

	/**
	 * 获取词典单子实例
	 *
	 * @return Dictionary 单例对象
	 */
	public static Dictionary getSingleton() {
		if (singleton == null) {
			throw new IllegalStateException("词典尚未初始化，请先调用initial方法");
		}
		return singleton;
	}

	/**
	 * 批量加载新词条
	 *
	 * @param words Collection&lt;String&gt;词条列表
	 */
	public void addWords(Collection<String> words) {
		if (words != null) {
			for (String word : words) {
				if (word != null) {
					//批量加载词条到主内存词典中
					singleton._MainDict.fillSegment(word.trim().toLowerCase().toCharArray());
				}
			}
		}
	}

	/**
	 * 批量移除（屏蔽）词条
	 *
	 * @param words a {@link java.util.Collection} object.
	 */
	public void disableWords(Collection<String> words) {
		if (words != null) {
			for (String word : words) {
				if (word != null) {
					//批量屏蔽词条
					singleton._MainDict.disableSegment(word.trim().toLowerCase().toCharArray());
				}
			}
		}
	}

	/**
	 * 检索匹配主词典
	 *
	 * @param charArray an array of char.
	 * @return Hit 匹配结果描述
	 */
	public Hit matchInMainDict(char[] charArray) {
		return singleton._MainDict.match(charArray);
	}

	/**
	 * 检索匹配主词典
	 *
	 * @param charArray an array of char.
	 * @param begin     a int.
	 * @param length    a int.
	 * @return Hit 匹配结果描述
	 */
	public Hit matchInMainDict(char[] charArray, int begin, int length) {
		return singleton._MainDict.match(charArray, begin, length);
	}

	/**
	 * 检索匹配量词词典
	 *
	 * @param charArray an array of char.
	 * @param begin     a int.
	 * @param length    a int.
	 * @return Hit 匹配结果描述
	 */
	public Hit matchInQuantifierDict(char[] charArray, int begin, int length) {
		return singleton._QuantifierDict.match(charArray, begin, length);
	}


	/**
	 * 从已匹配的Hit中直接取出DictSegment，继续向下匹配
	 *
	 * @param charArray    an array of char.
	 * @param currentIndex a int.
	 * @param matchedHit   a {@link org.wltea.analyzer.dic.Hit} object.
	 * @return Hit
	 */
	public Hit matchWithHit(char[] charArray, int currentIndex, Hit matchedHit) {
		DictSegment ds = matchedHit.getMatchedDictSegment();
		return ds.match(charArray, currentIndex, 1, matchedHit);
	}


	/**
	 * 判断是否是停止词
	 *
	 * @param charArray an array of char.
	 * @param begin     a int.
	 * @param length    a int.
	 * @return boolean
	 */
	public boolean isStopWord(char[] charArray, int begin, int length) {
		return singleton._StopWordDict.match(charArray, begin, length).isMatch();
	}

	/**
	 * 加载主词典及扩展词典
	 */
	private void loadMainDict() {
		//建立一个主词典实例
		_MainDict = new DictSegment((char) 0);
		//读取主词典文件
		String mainDictionary = cfg.getMainDictionary();
		doLoadLocalDict(_MainDict, Collections.singletonList(mainDictionary));
		//加载扩展词典
		this.loadExtDict();
	}

	/**
	 * 加载用户扩展的停止词词典
	 */
	private void loadStopWordDict() {
		//建立一个主词典实例
		_StopWordDict = new DictSegment((char) 0);
//		String stopWordDictionary = cfg.getStopWordDictionary();
		loadExtStopWordDict();
	}

	/**
	 * 加载用户配置的扩展词典到主词库表
	 */
	private void loadExtDict() {
		//加载扩展词典配置
		List<String> extDictFiles = cfg.getExtDictionarys();
		if (extDictFiles != null && extDictFiles.size() > 0) {
			doLoadLocalDict(_MainDict, extDictFiles);
		}
	}

	private void loadExtStopWordDict() {
		//加载扩展停止词典
		List<String> extStopWordDictFiles = cfg.getExtStopWordDictionarys();
		if (extStopWordDictFiles != null && extStopWordDictFiles.size() > 0) {
			doLoadLocalDict(_StopWordDict, extStopWordDictFiles);
		}
	}

	/**
	 * 加载量词词典
	 */
	private void loadQuantifierDict() {
		//建立一个量词典实例
		_QuantifierDict = new DictSegment((char) 0);
		doLoadLocalDict(_QuantifierDict, Collections.singletonList(cfg.getQuantifierDicionary()));
	}

	/**
	 * 加载远程扩展词典到主词库表
	 */
	private void loadRemoteExtDict() {
		List<String> remoteExtDictFiles = cfg.getRemoteExtDictionarys();
		doLoadRemoteDict(_MainDict, remoteExtDictFiles);
	}

	private void loadRemoteExtStopWordsDict() {
		List<String> remoteExtDictFiles = cfg.getRemoteExtStopWordDictionarys();
		doLoadRemoteDict(_MainDict, remoteExtDictFiles);
	}

	private void doLoadRemoteDict(DictSegment dictSegment, List<String> dictLocations) {
		for (String location : dictLocations) {
			System.out.println("加载远程扩展词典：" + location);
			List<String> lists = getRemoteWords(location);
			// 如果找不到扩展的字典，则忽略
			if (lists == null) {
				continue;
			}
			for (String theWord : lists) {
				System.out.println(location.substring(location.lastIndexOf("/")) + " -- " + theWord);
				if (theWord != null && !"".equals(theWord.trim())) {
					// 加载扩展词典数据到主内存词典中
					dictSegment.fillSegment(theWord.trim().toLowerCase().toCharArray());
				}
			}
		}

	}

	private void doLoadLocalDict(DictSegment dictSegment, List<String> dictLocations) {
		for (String dictLocation : dictLocations) {
			System.out.println("加载本地扩展词典：" + dictLocation);
			//读取主词典文件
			InputStream is = this.getClass().getClassLoader().getResourceAsStream(dictLocation);
			if (is == null) {
				throw new RuntimeException(dictLocation + " Dictionary not found!!!");
			}

			try {
				BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"), 512);
				String theWord = null;
				do {
					theWord = br.readLine();
					if (theWord != null && !"".equals(theWord.trim())) {
						dictSegment.fillSegment(theWord.trim().toLowerCase().toCharArray());
					}
				} while (theWord != null);

			} catch (IOException ioe) {
				System.err.println(dictLocation + " Dictionary loading exception.");
				ioe.printStackTrace();

			} finally {
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 从远程服务器上下载自定义词条
	 */
	private static List<String> getRemoteWords(String location) {

		List<String> buffer = new ArrayList<String>();
		RequestConfig rc = RequestConfig.custom().setConnectionRequestTimeout(10 * 1000).setConnectTimeout(10 * 1000)
				.setSocketTimeout(60 * 1000).build();
		CloseableHttpClient httpclient = HttpClients.createDefault();
		CloseableHttpResponse response;
		BufferedReader in;
		HttpGet get = new HttpGet(location);
		get.setConfig(rc);
		try {
			response = httpclient.execute(get);
			if (response.getStatusLine().getStatusCode() == 200) {

				String charset = "UTF-8";
				// 获取编码，默认为utf-8
				if (response.getEntity().getContentType().getValue().contains("charset=")) {
					String contentType = response.getEntity().getContentType().getValue();
					charset = contentType.substring(contentType.lastIndexOf("=") + 1);
				}
				in = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), charset));
				String line;
				while ((line = in.readLine()) != null) {
					buffer.add(line);
				}
				in.close();
				response.close();
				return buffer;
			}
			response.close();
		} catch (IllegalStateException | IOException e) {
			e.printStackTrace();
		}
		return buffer;
	}


	public void reLoadRemoteDict(DictType dictType, String location) {
		DictSegment dictSegment = null;
		switch (dictType) {
			case MainDict:
				dictSegment = _MainDict;
				break;
			case StopDist:
				dictSegment = _StopWordDict;
				break;
			case QuantifierDict:
				dictSegment = _QuantifierDict;
				break;
		}
		if (dictSegment == null) {
			return;
		}
		singleton.doLoadRemoteDict(dictSegment, Collections.singletonList(location));
	}

}
